//: C25:VirtualConstructor.cpp
// From Thinking in C++, 2nd Edition
// Available at http://www.BruceEckel.com
// (c) Bruce Eckel 1999
// Copyright notice in Copyright.txt
#include <iostream>
#include <string>
#include <exception>
#include <vector>
using namespace std;

class Shape
{
    Shape* s;
    // Prevent copy-construction & operator=
    Shape(Shape&);
    Shape operator=(Shape&);
protected:
    Shape() {
        s = 0;
    };
public:
    virtual void draw() {
        s->draw();
    }
    virtual void erase() {
        s->erase();
    }
    virtual void test() {
        s->test();
    };
    virtual ~Shape() {
        cout << "~Shape\n";
        if (s) {
            cout << "Making virtual call: ";
            s->erase(); // Virtual call
        }
        cout << "delete s: ";
        delete s; // The polymorphic deletion
    }
    class BadShapeCreation : public exception
    {
        string reason;
    public:
        BadShapeCreation(string type) {
            reason = "Cannot create type " + type;
        }
        const char *what() const {
            return reason.c_str();
        }
    };
    Shape(string type) throw(BadShapeCreation);
};

class Circle : public Shape
{
    Circle(Circle&);
    Circle operator=(Circle&);
    Circle() {} // Private constructor
    friend class Shape;
public:
    void draw() {
        cout << "Circle::draw\n";
    }
    void erase() {
        cout << "Circle::erase\n";
    }
    void test() {
        draw();
    }
    ~Circle() {
        cout << "Circle::~Circle\n";
    }
};

class Square : public Shape
{
    Square(Square&);
    Square operator=(Square&);
    Square() {}
    friend class Shape;
public:
    void draw() {
        cout << "Square::draw\n";
    }
    void erase() {
        cout << "Square::erase\n";
    }
    void test() {
        draw();
    }
    ~Square() {
        cout << "Square::~Square\n";
    }
};

Shape::Shape(string type)
throw(Shape::BadShapeCreation)
{
    if (type == "Circle")
        s = new Circle();
    else if (type == "Square")
        s = new Square();
    else throw BadShapeCreation(type);
    draw();  // Virtual call in the constructor
}

char* shlist[] = { "Circle", "Square", "Square",
                   "Circle", "Circle", "Circle", "Square", ""
                 };

int main()
{
    vector<Shape*> shapes;
    cout << "virtual constructor calls:" << endl;
    try {
        for (char** cp = shlist; **cp; cp++)
            shapes.push_back(new Shape(*cp));
    } catch (Shape::BadShapeCreation e) {
        cout << e.what() << endl;
        return 1;
    }
    for (int i = 0; i < shapes.size(); i++) {
        shapes[i]->draw();
        cout << "test\n";
        shapes[i]->test();
        cout << "end test\n";
        shapes[i]->erase();
    }
    Shape c("Circle"); // Create on the stack
    cout << "destructor calls:" << endl;
    for (int j = 0; j < shapes.size(); j++) {
        delete shapes[j];
        cout << "\n------------\n";
    }
} ///:~
